package cn.huaqingcheng.tool.core;

import cn.huaqingcheng.tool.core.model.Tuple;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 集合相关工具
 **/
@SuppressWarnings("unused")
public interface CollTool {

    Logger log = LoggerFactory.getLogger(CollTool.class);


    /**
     * 集合为空?
     */
    @Contract(value = "null -> true", pure = true)
    static boolean isEmpty(Collection<?> coll) {
        return coll == null || coll.isEmpty();
    }

    /**
     * 集合非空?
     */
    @Contract(value = "null -> false", pure = true)
    static boolean isNotEmpty(Collection<?> coll) {
        return coll != null && !coll.isEmpty();
    }

    /**
     * Map 是空?
     */
    @Contract(value = "null -> true", pure = true)
    static boolean isEmpty(Map<?, ?> map) {
        return map == null || map.isEmpty();
    }

    /**
     * Map 非空?
     */
    @Contract(value = "null -> false", pure = true)
    static boolean isNotEmpty(Map<?, ?> map) {
        return map != null && !map.isEmpty();
    }

    /**
     * 交集: 同时存在于两个集合中的元素
     *
     * @param left  不含 null 的原始集合
     * @param right 不含 null 的原始集合
     * @return 两个集合的交集; 如果任意集合为空/null 原样返回另外一个集合 或 空对象
     */
    static <T> List<T> intersection(List<T> left, List<T> right) {
        return intersection(left, (Collection<T>) right).toList();
    }

    /**
     * 交集
     */
    private static <T, C extends Collection<T>> Stream<T> intersection(Collection<T> left, Collection<T> right) {
        boolean rightIsEmpty = right == null || right.isEmpty();
        if (left == null || left.isEmpty()) {
            return rightIsEmpty ? Stream.empty() : right.stream();
        }
        if (rightIsEmpty) {
            return left.stream();
        }

        Set<T> rightSet = new HashSet<>(right);

        return left.stream()
            .filter(rightSet::contains)
            .distinct();
    }

    /**
     * 交集: 同时存在于两个集合中的元素
     *
     * @param left  不含 null 的原始集合
     * @param right 不含 null 的原始集合
     * @return 两个集合的交集; 如果任意集合为空/null 原样返回另外一个集合 或 空对象
     */
    static <T> Set<T> intersection(Set<T> left, Set<T> right) {
        return intersection(left, (Collection<T>) right).collect(Collectors.toSet());
    }

    /**
     * 交集: 同时存在于两个集合中的元素
     *
     * @param left  不含 null 的原始集合
     * @param right 不含 null 的原始集合
     * @return 两个集合的交集; 如果任意集合为空/null 原样返回另外一个集合 或 空对象
     */
    static <K, V> Map<K, V> intersection(Map<K, V> left, Map<K, V> right) {
        boolean rightIsEmpty = right == null || right.isEmpty();
        if (left == null || left.isEmpty()) {
            return rightIsEmpty ? Collections.emptyMap() : right;
        }
        if (rightIsEmpty) {
            return left;
        }

        return left.entrySet()
            .parallelStream()
            .filter(entry -> right.containsKey(entry.getKey()))
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    }

    /**
     * 并集 去重
     */
    static <T> List<T> union(List<T> left, List<T> right) {
        return union(left, (Collection<T>) right).toList();
    }

    /**
     * 并集 去重
     */
    private static <T, C extends Collection<T>> Stream<T> union(C left, C right) {
        boolean rightIsEmpty = right == null || right.isEmpty();
        if (left == null || left.isEmpty()) {
            return rightIsEmpty ? Stream.empty() : right.stream();
        }
        if (rightIsEmpty) {
            return left.stream();
        }

        return Stream.of(left, right)
            .flatMap(Collection::stream)
            .distinct();
    }

    /**
     * 并集 去重
     */
    static <T> Set<T> union(Set<T> left, Set<T> right) {
        return union(left, (Collection<T>) right).collect(Collectors.toSet());
    }

    /**
     * 并集 不去重
     */
    static <T> List<T> unionAll(List<T> left, List<T> right) {
        boolean rightIsEmpty = right == null || right.isEmpty();
        if (left == null || left.isEmpty()) {
            return rightIsEmpty ? Collections.emptyList() : right;
        }
        if (rightIsEmpty) {
            return left;
        }

        return Stream.of(left, right)
            .flatMap(Collection::stream)
            .toList();
    }

    /**
     * 差集
     *
     * @return {@link Tuple.Tuple2#a()} 为 "左差集": 仅存在于 {@code left} 中的元素 <br>
     * {@link Tuple.Tuple2#b()} 为 "右差集": 仅存在于 {@code right} 中的元素
     */
    static <T> Tuple.Tuple2<List<T>, List<T>> diffSet(List<T> left, List<T> right) {
        Tuple.Tuple2<Stream<T>, Stream<T>> diffSet = diffSet(left, (Collection<T>) right);
        return Tuple.map(diffSet, Stream::toList);
    }

    /**
     * 差集
     *
     * @return {@link Tuple.Tuple2#a()} 为 "左差集": 仅存在于 {@code left} 中的元素 <br>
     * {@link Tuple.Tuple2#b()} 为 "右差集": 仅存在于 {@code right} 中的元素
     */
    static <T, C extends Collection<T>> Tuple.Tuple2<Stream<T>, Stream<T>> diffSet(C left, C right) {
        boolean rightIsEmpty = right == null || right.isEmpty();
        if (left == null || left.isEmpty()) {
            return rightIsEmpty
                   ? Tuple.of(Stream.empty(), Stream.empty())
                   : Tuple.of(Stream.empty(), right.stream());
        }
        if (rightIsEmpty) {
            return Tuple.of(left.stream(), Stream.empty());
        }

        Stream<T> lStream = left.stream()
            .filter(l -> !right.contains(l));
        Stream<T> rStream = right.stream()
            .filter(r -> !left.contains(r));

        return Tuple.of(lStream, rStream);
    }

    /**
     * 差集
     *
     * @return {@link Tuple.Tuple2#a()} 为 "左差集": 仅存在于 {@code left} 中的元素 <br>
     * {@link Tuple.Tuple2#b()} 为 "右差集": 仅存在于 {@code right} 中的元素
     */
    static <T> Tuple.Tuple2<Set<T>, Set<T>> diffSet(Set<T> left, Set<T> right) {
        Tuple.Tuple2<Stream<T>, Stream<T>> diffSet = diffSet(left, (Collection<T>) right);
        return Tuple.map(diffSet, i -> i.collect(Collectors.toSet()));
    }

    @Contract(value = "_, null -> fail; _, !null -> new", pure = true)
    static <T, R> List<R> map(@NotNull Collection<T> source, Function<T, R> map) {
        return source.stream().map(map).toList();
    }

    /**
     * 过滤后去重
     *
     * @see #distinct(List)
     * @see #mapDistinct(Collection, Function)
     */
    @Contract(value = "_, null -> fail; _, !null -> new", pure = true)
    static <E> List<E> filter(@NotNull Collection<E> source, Predicate<E> filter) {
        return source.stream().filter(filter).distinct().toList();
    }

    /**
     * 映射后去重
     *
     * @see #distinct(List)
     * @see #filter(Collection, Predicate)
     */
    static <T, R> List<R> mapDistinct(@NotNull Collection<T> source, Function<T, R> map) {
        return source.stream().map(map).distinct().toList();
    }

    /**
     * 仅去重
     *
     * @see #filter(Collection, Predicate)
     */
    @Contract(pure = true)
    static <E> List<E> distinct(@NotNull List<E> list) {
        return list.stream().distinct().toList();
    }

    @Contract(pure = true)
    static <E, R> List<R> filterMap(@NotNull Collection<E> source, Predicate<E> filter, Function<E, R> map) {
        return source.stream().filter(filter).map(map).toList();
    }

    @Contract(pure = true)
    static <E, K, M extends Function<E, K>> Map<K, E> toMap(Collection<E> source, M keyMap) {
        return toMap(source, keyMap, Function.identity());
    }

    @Contract(pure = true)
    static <E, K, V> Map<K, V> toMap(@NotNull Collection<E> source, Function<E, K> keyMap, Function<E, V> valueMap) {
        return source.stream()
            .collect(Collectors.toMap(keyMap, valueMap));
    }

    @Contract(pure = true)
    static <E> int count(@NotNull List<E> list, E e) {
        long count = list.stream()
            .filter(i -> Objects.equals(i, e))
            .count();
        return Math.toIntExact(count);
    }

    @Contract(pure = true)
    static <E> int filterCount(@NotNull List<E> list, Predicate<? super E> predicate) {
        long count = list.stream()
            .filter(predicate)
            .count();
        return Math.toIntExact(count);
    }

    /**
     * 分组映射 <br>
     * 对每一组数据独立执行一次 map 映射, 丢弃 分组的 key 返回所有 映射结果
     *
     * @param source  源列表, 非 null
     * @param groupBy 分组依据
     * @param map     分组后的每一个子列表映射
     * @param <E>     源列表元素类型
     * @param <K>     分组依据返回值类型
     * @param <R>     最终返回值类型
     * @return map 返回值的汇总
     */
    @Contract(pure = true)
    static <E, K, R> List<R> groupToValueList(@NotNull List<E> source, Function<E, K> groupBy, Function<List<E>, R> map) {
        return source.stream()
            .filter(i -> {
                boolean b = Objects.nonNull(groupBy.apply(i));
                if (!b) {
                    log.warn("group map 的 key 不能为 Null, 已过滤掉: {}", i);
                }
                return b;
            })
            .collect(Collectors.groupingBy(groupBy))
            .values().stream().map(map).toList();
    }

    /**
     * {@link #group(List, Function, Function, Function)} 的特例, 二次分组的列表直接返回
     */
    static <E, K, R> Map<K, Map<R, List<E>>> group(List<E> source, Function<E, K> group1, Function<E, R> group2) {
        return group(source, group1, group2, Function.identity());
    }

    /**
     * 连续两次分组映射, 转双层 map
     *
     * @param source   原列表
     * @param group1   第一次分组依据
     * @param group2   第一次分组的列表 再次分组
     * @param valueMap 最终值映射
     * @param <E>      原列表元素
     * @param <K>      第一层的 key
     * @param <R>      第二层的 key
     * @param <V>      第二层 map 的值
     * @return 双层 map
     */
    static <E, K, R, V> Map<K, Map<R, V>> group(
        List<E> source,
        Function<E, K> group1,
        Function<E, R> group2,
        Function<List<E>, V> valueMap
    ) {
        return groupToMap(
            source,
            group1,
            list -> groupToMap(list, group2, valueMap)
        );
    }

    /**
     * 分组映射 <br>
     * 返回分组的 key -> 每组 value 再次映射的结果集
     */
    static <E, K, R> Map<K, R> groupToMap(@NotNull List<E> source, Function<E, K> groupBy, Function<List<E>, R> map) {
        return source.stream()
            .collect(Collectors.groupingBy(groupBy))
            .entrySet()
            .stream()
            .map(entry -> Tuple.of(entry.getKey(), map.apply(entry.getValue())))
            .collect(Collectors.toMap(Tuple.Tuple2::a, Tuple.Tuple2::b));
    }

    @Contract(pure = true)
    static <E> BigDecimal sum(@NotNull List<E> source, Function<E, BigDecimal> map) {
        return source.stream()
            .map(map)
            .reduce(BigDecimal::add)
            .orElse(BigDecimal.ZERO);
    }

    @Contract(pure = true)
    static BigDecimal sum(@NotNull List<BigDecimal> list) {
        return list.stream()
            .reduce(BigDecimal::add)
            .orElse(BigDecimal.ZERO);
    }

}
